#ifndef _META_FUNCTOR_IMPL_H_
#define _META_FUNCTOR_IMPL_H_

#include <typeinfo>
#include "Meta.Typelist.h"
#include "Meta.TypeTraits.h"

namespace Maha
{
      namespace Meta
      {
    namespace Private
    {
        template <typename ResultType>
        struct FunctorImplBase 
        {
            typedef FunctorImplBase<ResultType> FunctorImplBaseType;

            typedef EmptyType Parm1;
            typedef EmptyType Parm2;
            typedef EmptyType Parm3;
            typedef EmptyType Parm4;
            typedef EmptyType Parm5;
			
            virtual ~FunctorImplBase() {}

            virtual FunctorImplBase* DoClone() const = 0;

            template <class U>
            static U* Clone(U* pObj)
            {
                if (!pObj) return 0;
                U* pClone = static_cast<U*>(pObj->DoClone());
                assert(typeid(*pClone) == typeid(*pObj));
                return pClone;
            }
         
        };
    }
    
	template <typename T>
	struct Clonable
	{
		T* DoClone() const
		{
			return new T(*this);
		}
	};
	
    template <typename ResultType, class TList>
    class FunctorImpl;

    template <typename R>
    class FunctorImpl<R, NullType>
        : public Private::FunctorImplBase<R>
    {
    public:
		typedef R ResultType;
        virtual ResultType operator()() = 0;
    };
	
    template <typename R, typename P1>
    class FunctorImpl<R, TYPELIST_1(P1)>
        : public Private::FunctorImplBase<R>
    {
    public:
		typedef R ResultType;
        typedef typename TypeTraits<P1>::ParameterType Parm1;
		
        virtual ResultType operator()(Parm1) = 0;
    };

    template <typename R, typename P1, typename P2>
    class FunctorImpl<R, TYPELIST_2(P1, P2)>
        : public Private::FunctorImplBase<R>
    {
    public:
		typedef R ResultType;
        typedef typename TypeTraits<P1>::ParameterType Parm1;
        typedef typename TypeTraits<P2>::ParameterType Parm2;
		
        virtual ResultType operator()(Parm1, Parm2) = 0;
    };

    template <typename R, typename P1, typename P2, typename P3>
    class FunctorImpl<R, TYPELIST_3(P1, P2, P3)>
        : public Private::FunctorImplBase<R>
    {
    public:
		typedef R ResultType;
        typedef typename TypeTraits<P1>::ParameterType Parm1;
        typedef typename TypeTraits<P2>::ParameterType Parm2;
        typedef typename TypeTraits<P3>::ParameterType Parm3;
		
        virtual ResultType operator()(Parm1, Parm2, Parm3) = 0;
    };

    template <typename R, typename P1, typename P2, typename P3, typename P4>
    class FunctorImpl<R, TYPELIST_4(P1, P2, P3, P4)>
        : public Private::FunctorImplBase<R>
    {
    public:
		typedef R ResultType;
        typedef typename TypeTraits<P1>::ParameterType Parm1;
        typedef typename TypeTraits<P2>::ParameterType Parm2;
        typedef typename TypeTraits<P3>::ParameterType Parm3;
        typedef typename TypeTraits<P4>::ParameterType Parm4;
		
        virtual ResultType operator()(Parm1, Parm2, Parm3, Parm4) = 0;
    };

    template <class ParentFunctor, typename Fun>
    class FunctorHandler : public ParentFunctor::Impl
    {
        typedef typename ParentFunctor::Impl Base;

    public:
        typedef typename Base::ResultType ResultType;
        typedef typename Base::Parm1 Parm1;
        typedef typename Base::Parm2 Parm2;
        typedef typename Base::Parm3 Parm3;
        typedef typename Base::Parm4 Parm4;
        
        FunctorHandler(const Fun& fun) : f_(fun) {}
                
        ResultType operator()()
        {
			return f_();
		}

        ResultType operator()(Parm1 p1)
        {
			return f_(p1);
		}
        
        ResultType operator()(Parm1 p1, Parm2 p2)
        {
			return f_(p1, p2);
		}
        
        ResultType operator()(Parm1 p1, Parm2 p2, Parm3 p3)
        {
			return f_(p1, p2, p3);
		}
        
        ResultType operator()(Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4)
        {
			return f_(p1, p2, p3, p4);
		}
        
		FunctorHandler* DoClone() const
		{
			return new FunctorHandler(*this);
		}
		
    private:
        Fun f_;
    };
        

    template <class ParentFunctor, typename PointerToObj, typename PointerToMemFn>
	class MemFunHandler : public ParentFunctor::Impl
    {
        typedef typename ParentFunctor::Impl Base;

    public:
        typedef typename Base::ResultType ResultType;
        typedef typename Base::Parm1 Parm1;
        typedef typename Base::Parm2 Parm2;
        typedef typename Base::Parm3 Parm3;
        typedef typename Base::Parm4 Parm4;

        MemFunHandler(const PointerToObj& pObj, PointerToMemFn pMemFn) 
        : pObj_(pObj), pMemFn_(pMemFn) {}

        ResultType operator()()
        {
			return ((*pObj_).*pMemFn_)();
		}

        ResultType operator()(Parm1 p1)
        {
			return ((*pObj_).*pMemFn_)(p1);
		}
        
        ResultType operator()(Parm1 p1, Parm2 p2)
        {
			return ((*pObj_).*pMemFn_)(p1, p2);
		}
        
        ResultType operator()(Parm1 p1, Parm2 p2, Parm3 p3)
        {
			return ((*pObj_).*pMemFn_)(p1, p2, p3);
		}
        
        ResultType operator()(Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4)
        {
			return ((*pObj_).*pMemFn_)(p1, p2, p3, p4);
		}
        
		MemFunHandler* DoClone() const
		{
			return new MemFunHandler(*this);
		}
		
    private:
        PointerToObj pObj_;
        PointerToMemFn pMemFn_;
    };
    
	/*
    class bad_function_call : public std::runtime_error
    {
    public:
        bad_function_call() : std::runtime_error("bad_function_call in Loki::Functor") {}
    };
	*/
	
      } // Meta
} // Maha

#endif // _META_FUNCTOR_IMPL_H_
